/*
* File: CFdEventMonitor.cpp
* Copyright (c) 2010. Silviu Caragea <silviu@intelliproject.net>
* Created on September 14, 2010, 10:39 PM
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*  1.Redistributions of source code must retain the above copyright
*    notice, this list of conditions and the following disclaimer.
*  2.Redistributions in binary form must reproduce the above copyright
*    notice, this list of conditions and the following disclaimer in the
*    documentation and/or other materials provided with the distribution.
*  3.All advertising materials mentioning features or use of this software
*    must display the following acknowledgement:
*       This product includes software developed by the University of
*       California, Berkeley and its contributors.
*  4.Neither the name of the University nor the names of its contributors
*    may be used to endorse or promote products derived from this software
*    without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED.IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include "CFdEventMonitor-Inl.h"

namespace litevent
{

/*ordered by performances*/

static const struct EventMothodSignature *evMethods[] =
{

#ifdef USE_SOLARIS_DEVPOLL
    &sigDevPoll,
#endif

#ifdef USE_KQUEUE
    &sigKqueue,
#endif

#ifdef USE_EPOLL
    &sigEpoll,
#endif

#ifdef USE_POLL
    &sigPoll,
#endif

#ifdef HAVE_SELECT
    &sigSelect,
#endif

    NULL
};

CFdEventMonitor::CFdEventMonitor(EnumEventHandlerType eventPollType, size_t maxfds ) :methodName(NULL)
{

#ifdef HAVE_MULTITHREAD_LITEVENT
    m_lock.lock();
#endif

    this->pollType = eventPollType;

    /*pick first notification system. (are ordered by performance)*/
    if(this->pollType == EnumEventHandlerTypeUnset)
    {
         const struct EventMothodSignature **method = &evMethods[0];

         if(*method == NULL)
         {
             util::CLogger::sharedLogger()->WriteFormated("%s.%d: No available notification system!", __FILE__, __LINE__);
         }
         else
         {
             this->pollType = (*method)->type;
             this->methodName = (*method)->name;
             this->evInterface = CFdEventFactory::CreateInstance(this->pollType,maxfds);
         }
    }
    else
    {
        this->evInterface = CFdEventFactory::CreateInstance(this->pollType, maxfds);
    }

#ifdef HAVE_MULTITHREAD_LITEVENT
    m_lock.unlock();
#endif

}

CFdEventMonitor::~CFdEventMonitor()
{
    SafeReleasePtr(this->evInterface);
}

void CFdEventMonitor::Loop(int attributes )
{  
    /*
        timeout < 0 wait until a signal appear
        timeout = 0 return immediatly
        timeout > 0 max waiting time
    */

    while (evInterface->getWatchedEvents())
    {
        int n = 0;

        if (attributes & EVENT_LOOP_NONBLOCK)
        {
            n = evInterface->fdPoll(0); //return immediatly
        }
        else
        {
            n = evInterface->fdPoll(-1); //wait infinitly
        }

        if (n > 0)
        {
            /* n is the number of events */

            int revents;
            int fdIndex = -1;

            do
            {
                fdIndex = evInterface->fdGetNextFileDescriptor(fdIndex);

                if (fdIndex == -1)
                    break;

                revents = evInterface->fdGetReturnedEvents(fdIndex);
                int fd = evInterface->fdGetFileDescriptor(fdIndex);
                fd_event_handler handler = evInterface->fdGetHandler(fd);
                void *context = evInterface->fdGetContext(fd);
                
                SignalEvent *signalEvent= evInterface->fdGetSignalSlot(fd);

                if(signalEvent)
                    signalEvent->emit(fd,context,revents);
                
                if(*handler)
                    (*handler)(fd, context, revents);

            }
            while (--n > 0);
        }
#if 1
        else if(n < 0)
        {
            util::CLogger::sharedLogger()->WriteFormated("%s.%d: Poll error=>%s", __FILE__, __LINE__,strerror(errno));
            break;
        }
#endif
        if (attributes & (EVENT_LOOP_NONBLOCK | EVENT_LOOP_ONCE))
            break;

    }
}

int CFdEventMonitor::QuickPoll(int timeOut)
{
    int n = evInterface->fdPoll(timeOut);

    if (n > 0)
    {
        /* n is the number of events */

        int revents;
        int fdIndex = -1;

        do
        {
            fdIndex = evInterface->fdGetNextFileDescriptor(fdIndex);

            if (fdIndex == -1)
                break;

            revents = evInterface->fdGetReturnedEvents(fdIndex);
            int fd = evInterface->fdGetFileDescriptor(fdIndex);
            fd_event_handler handler = evInterface->fdGetHandler(fd);
            void *context = evInterface->fdGetContext(fd);

            SignalEvent *signalEvent= evInterface->fdGetSignalSlot(fd);

            if(signalEvent)
                signalEvent->emit(fd,context,revents);

            if(*handler)
                (*handler)(fd, context, revents);

        }
        while (--n > 0);
    }
#if 1
    else if(n < 0)
    {
        util::CLogger::sharedLogger()->WriteFormated("%s.%d: Poll error=>%s", __FILE__, __LINE__,strerror(errno));
        return 0;
    }
#endif

    return 1;
}


const char** CFdEventMonitor::getAvailableMethods()
{
    static const char **methods = NULL;
    const struct EventMothodSignature **method;
    const char **tmp;
    int i = 0, k;

    /* count all methods */
    for (method = &evMethods[0]; *method != NULL; ++method)
	++i;

    /* allocate one more than we need for the NULL pointer */

    tmp = (const char**)calloc((i + 1), sizeof(char *));

    if (tmp == NULL)
	return NULL;

    /* populate the array with the supported methods */

    for (k = 0, i = 0; evMethods[k] != NULL; ++k)
    {
	tmp[i++] = evMethods[k]->name;
    }

    tmp[i] = NULL;

    if (methods != NULL)
    	free((char**)methods);

    methods = tmp;

    return (methods);
}

const char * CFdEventMonitor::getCurrentMethod()
{
    /*probably we can remove the critical section fromn this method*/

#ifdef HAVE_MULTITHREAD_LITEVENT
    m_lock.lock();
#endif

    if(methodName== NULL)
    {
        const struct EventMothodSignature **method;

        for (method = &evMethods[0]; *method != NULL; ++method)
            if((*method)->type == this->pollType)
            {
                methodName = (*method)->name;
                break;
            }
    }

#ifdef HAVE_MULTITHREAD_LITEVENT
    m_lock.unlock();
#endif

    return methodName;
}

}
